package cn.aws360.boot.system.biz.service.impl;

import cn.aws360.boot.base.api.constant.enums.DelFlagEnum;
import cn.aws360.boot.base.api.constant.enums.SysFlagEnum;
import cn.aws360.boot.base.api.exception.ServiceException;
import cn.aws360.boot.data.api.constant.DataConstant;
import cn.aws360.boot.data.api.factory.PageFactory;
import cn.aws360.boot.data.api.pojo.request.PageQuery;
import cn.aws360.boot.data.api.pojo.response.PageResult;
import cn.aws360.boot.data.api.utils.PojoUtils;
import cn.aws360.boot.system.api.constant.SysConstant;
import cn.aws360.boot.system.api.exception.SysExceptionEnum;
import cn.aws360.boot.system.api.pojo.request.SysConfQuery;
import cn.aws360.boot.system.api.pojo.request.SysConfWrite;
import cn.aws360.boot.system.api.pojo.response.SysConfModel;
import cn.aws360.boot.system.biz.domain.SysConf;
import cn.aws360.boot.system.biz.mapper.SysConfMapper;
import cn.aws360.boot.system.biz.service.SysConfService;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.List;

/**
 * @author junnan
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SysConfServiceImpl extends ServiceImpl<SysConfMapper, SysConf> implements SysConfService {

    private final CacheManager cacheManager;

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = SysConstant.MODULE_CONF_CACHE, allEntries = true)
    public Boolean add(SysConfWrite sysConfWrite) {
        SysConf sysConf = BeanUtil.copyProperties(sysConfWrite, SysConf.class, DataConstant.ID);
        checkCodeHasExist(sysConf);
        return this.save(sysConf);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = SysConstant.MODULE_CONF_CACHE, allEntries = true)
    public Boolean edit(SysConfWrite sysConfWrite) {
        SysConf sysConf = this.getSysConfById(sysConfWrite.getId());
        BeanUtil.copyProperties(sysConfWrite, sysConf);
        checkCodeHasExist(sysConf);
        return this.updateById(sysConf);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = SysConstant.MODULE_CONF_CACHE, allEntries = true)
    public Boolean remove(String ids) {
        List<Integer> confIdList = Convert.toList(Integer.class, ids.split(StrUtil.COMMA));
        this.listByIds(confIdList).forEach(this::checkSysDontDelete);
        return this.removeByIds(confIdList);
    }

    @Override
    public SysConfModel detail(Integer id) {
        SysConf sysConf = this.getSysConfById(id);
        return BeanUtil.copyProperties(sysConf, SysConfModel.class);
    }

    @Override
    public List<SysConfModel> findList(SysConfQuery sysConfQuery) {
        LambdaQueryWrapper<SysConf> queryWrapper = createQueryWrapper(sysConfQuery);
        List<SysConf> sysConfList = this.list(queryWrapper);
        return PojoUtils.copyToList(sysConfList, SysConfModel.class);
    }

    @Override
    public PageResult<SysConfModel> findPage(SysConfQuery sysConfQuery, PageQuery pageQuery) {
        LambdaQueryWrapper<SysConf> queryWrapper = createQueryWrapper(sysConfQuery);
        PageResult<SysConf> sysConfPage = PageFactory.getPageResult(this, pageQuery, queryWrapper);
        return PojoUtils.copyToPage(sysConfPage, SysConfModel.class);
    }

    @Override
    @PostConstruct
    public Boolean refreshCache() {
        Cache cache = cacheManager.getCache(SysConstant.MODULE_CONF_CACHE);
        assert cache != null;
        cache.clear();
        for (SysConf sysConf : this.list()) {
            cache.put(sysConf.getCode(), sysConf.getValue());
        }
        return true;
    }

    @Override
    @Cacheable(value = SysConstant.MODULE_CONF_CACHE, key = "#code")
    public String getValueByCode(String code) {
        SysConf sysConf = this.getOne(Wrappers.<SysConf>lambdaQuery().eq(SysConf::getCode, code));
        checkNotExist(sysConf);
        return sysConf.getValue();
    }

    private SysConf getSysConfById(Integer id) {
        SysConf sysConf = this.getById(id);
        checkNotExist(sysConf);
        return sysConf;
    }

    private LambdaQueryWrapper<SysConf> createQueryWrapper(SysConfQuery confQuery) {
        return Wrappers.<SysConf>lambdaQuery()
                .like(ObjectUtil.isNotEmpty(confQuery.getName()), SysConf::getName, confQuery.getName())
                .like(ObjectUtil.isNotEmpty(confQuery.getCode()), SysConf::getCode, confQuery.getCode())
                .orderByAsc(SysConf::getId);
    }

    private void checkNotExist(SysConf sysConf) {
        if (ObjectUtil.isNull(sysConf) || DelFlagEnum.Y.getValue().equals(sysConf.getDelFlag())) {
            throw new ServiceException(SysExceptionEnum.CONF_NOT_EXIST);
        }
    }

    private void checkCodeHasExist(SysConf sysConf) {
        Integer id = sysConf.getId();
        String code = sysConf.getCode();
        id = (ObjectUtil.isNotNull(id) && id != 0) ? id : -1;
        SysConf existConf = this.getOne(Wrappers.<SysConf>lambdaQuery().eq(SysConf::getCode, code));
        if (ObjectUtil.isNotNull(existConf) && !existConf.getId().equals(id)) {
            throw new ServiceException(SysExceptionEnum.CONF_CODE_HAS_EXIST, code);
        }
    }

    private void checkSysDontDelete(SysConf sysConf) {
        if (SysFlagEnum.Y.getValue().equals(sysConf.getSysFlag())) {
            throw new ServiceException(SysExceptionEnum.CONF_DONT_DELETE_FOR_SYS, sysConf.getId());
        }
    }

}
